#include "TestDocumentStreamReader.h"

#include "LeDocumentObject/DocumentStreamReader.h"
#include "LeDocumentObject/Document.h"
#include "LeDocumentObject/IDocumentObject.h"

#include "DocumentObjectMock.h"
#include "DocumentObjectFactoryMock.h"


#include <QBuffer>
#include <QTest>
#include <QVariant>
#include <QXmlStreamReader>

#include <iostream>

void TestDocumentStreamReader::testReadStringValue_data()
{
    QTest::addColumn<QString>("input");
    QTest::addColumn<bool>("success");
    QTest::addColumn<QString>("value");

    QTest::newRow("Simple") << "foo bar baz" << true << "foo bar baz";
    QTest::newRow("With new lines") << "foo\tbar\nbaz\n" << true << "foo\tbar\nbaz\n";
}

void TestDocumentStreamReader::testReadStringValue()
{
    QFETCH(QString, input);
    QFETCH(bool, success);
    QFETCH(QString, value);
    LDO::DocumentStreamReader reader;

    const QString inputString = QString("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                                        "%1"
                                        "</test>").arg(input);
    const QByteArray data = inputString.toUtf8();
    QBuffer buffer;
    buffer.setData(data);
    buffer.open(QBuffer::ReadOnly);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readStringValue();

    QCOMPARE(result.isValid(), success);
    if (success)
    {
        QCOMPARE(result.type(), QVariant::String);
        QCOMPARE(result.toString(), value);
    }
}

void TestDocumentStreamReader::testReadDoubleValue_data()
{
    QTest::addColumn<QString>("input");
    QTest::addColumn<bool>("success");
    QTest::addColumn<double>("value");

    QTest::newRow("Simple") << "1.234" << true << 1.234;
    QTest::newRow("Integer") << "-42" << true << -42.0;
    QTest::newRow("Any string") << "foobarbaz" << false << 0.0;
}

void TestDocumentStreamReader::testReadDoubleValue()
{
    QFETCH(QString, input);
    QFETCH(bool, success);
    QFETCH(double, value);

    LDO::DocumentStreamReader reader;
    const QString inputString = QString("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                                        "%1"
                                        "</test>").arg(input);
    const QByteArray data = inputString.toUtf8();
    QBuffer buffer;
    buffer.setData(data);
    buffer.open(QBuffer::ReadOnly);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readDoubleValue();

    QCOMPARE(result.isValid(), success);
    if (success)
    {
        QCOMPARE(result.type(), QVariant::Double);
        QCOMPARE(result.toDouble(), value);
    }
}


void TestDocumentStreamReader::testReadIntValue_data()
{
    QTest::addColumn<QString>("input");
    QTest::addColumn<bool>("success");
    QTest::addColumn<int>("value");

    QTest::newRow("42") << "42" << true << 42;
    QTest::newRow("-42") << "-42" << true << -42;
    QTest::newRow("Int min") << "-2147483648" << true << INT_MIN;
    QTest::newRow("Int max") << "2147483647" << true << INT_MAX;
    QTest::newRow("Any double") << "0.235" << false << 0;
    QTest::newRow("Any string") << "foo bar baz" << false << 0;
}

void TestDocumentStreamReader::testReadIntValue()
{
    QFETCH(QString, input);
    QFETCH(bool, success);
    QFETCH(int, value);

    LDO::DocumentStreamReader reader;
    const QString inputString = QString("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                                        "%1"
                                        "</test>").arg(input);
    const QByteArray data = inputString.toUtf8();
    QBuffer buffer;
    buffer.setData(data);
    buffer.open(QBuffer::ReadOnly);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readIntValue();

    QCOMPARE(result.isValid(), success);
    if (success)
    {
        QCOMPARE(result.type(), QVariant::Int);
        QCOMPARE(result.toInt(), value);
    }
}

void TestDocumentStreamReader::testReadBoolValue_data()
{

    QTest::addColumn<QString>("input");
    QTest::addColumn<bool>("success");
    QTest::addColumn<bool>("value");

    QTest::newRow("true") << "true" << true << true;
    QTest::newRow("false") << "false" << true << false;
    QTest::newRow("True") << "True" << true << true;
    QTest::newRow("Flase") << "False" << true << false;
    QTest::newRow("1") << "1" << true << true;
    QTest::newRow("0") << "0" << true << false;
    QTest::newRow("Any integer") << "-42" << true << true; // FIXME
    QTest::newRow("Any double") << "0.235" << true << true; // FIXME
    QTest::newRow("Any string") << "foo bar baz" << true << true; // FIXME
}

void TestDocumentStreamReader::testReadBoolValue()
{
    QFETCH(QString, input);
    QFETCH(bool, success);
    QFETCH(bool, value);

    LDO::DocumentStreamReader reader;
    const QString inputString = QString("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                                        "%1"
                                        "</test>").arg(input);
    const QByteArray data = inputString.toUtf8();
    QBuffer buffer;
    buffer.setData(data);
    buffer.open(QBuffer::ReadOnly);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readBoolValue();

    QCOMPARE(result.isValid(), success);
    if (success)
    {
        QCOMPARE(result.type(), QVariant::Bool);
        QCOMPARE(result.toBool(), value);
    }
}

void TestDocumentStreamReader::testReadEnumValue()
{
    LDO::DocumentStreamReader reader;
    const QString inputString = QString("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                                        "MockEnum8"
                                        "</test>");
    const QByteArray data = inputString.toUtf8();
    QBuffer buffer;
    buffer.setData(data);
    buffer.open(QBuffer::ReadOnly);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readEnumValue(QMetaType::type("DocumentObjectMock1::MockEnum"),
                                           DocumentObjectMock1::staticMetaObject.enumerator(0));

    QCOMPARE(result.isValid(), true);
    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), QMetaType::type("DocumentObjectMock1::MockEnum"));
    QCOMPARE(result.value<DocumentObjectMock1::MockEnum>(), DocumentObjectMock1::MockEnum8);
}

void TestDocumentStreamReader::testReadFlagValue()
{
    LDO::DocumentStreamReader reader;
    const QString inputString = QString("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                                        "MockFlag1|MockFlag3"
                                        "</test>");
    const QByteArray data = inputString.toUtf8();
    QBuffer buffer;
    buffer.setData(data);
    buffer.open(QBuffer::ReadOnly);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readFlagValue(QMetaType::type("DocumentObjectMock1::MockFlags"),
                                           DocumentObjectMock1::staticMetaObject.enumerator(1));

    QCOMPARE(result.isValid(), true);
    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), QMetaType::type("DocumentObjectMock1::MockFlags"));
    QCOMPARE(result.value<DocumentObjectMock1::MockFlags>(), DocumentObjectMock1::MockFlag1|DocumentObjectMock1::MockFlag3);
}

void TestDocumentStreamReader::testReadPointValue()
{
    LDO::DocumentStreamReader reader;
    const QString inputString = QString("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                                        "<x>1.2</x><y>3.4</y>"
                                        "</value>");
    const QByteArray data = inputString.toUtf8();
    QBuffer buffer;
    buffer.setData(data);
    buffer.open(QBuffer::ReadOnly);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readPointValue();

    QCOMPARE(result.isValid(), true);
    QCOMPARE(result.type(), QVariant::PointF);
    QCOMPARE(result.value<QPointF>(), QPointF(1.2, 3.4));
}

void TestDocumentStreamReader::testReadSizeValue()
{
    LDO::DocumentStreamReader reader;
    const QString inputString = QString("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                                        "<width>1.2</width><height>3.4</height>"
                                        "</test>");
    const QByteArray data = inputString.toUtf8();
    QBuffer buffer;
    buffer.setData(data);
    buffer.open(QBuffer::ReadOnly);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readSizeValue();

    QCOMPARE(result.isValid(), true);
    QCOMPARE(result.type(), QVariant::SizeF);
    QCOMPARE(result.value<QSizeF>(), QSizeF(1.2, 3.4));
}

void TestDocumentStreamReader::testReadStringListValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<string>foo</string>"
                    "<string>bar</string>"
                    "<string>baz</string>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readStringListValue();

    QCOMPARE(result.type(), QVariant::StringList);
    QCOMPARE(result.value<QStringList>(), QStringList() << "foo" << "bar" << "baz");
}

void TestDocumentStreamReader::testReadListOfStringValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<item>foo</item>"
                    "<item>bar</item>"
                    "<item>baz</item>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readListOfStringValue();

    int metaType = qRegisterMetaType<QList<QString>>();
    QVERIFY(metaType != QMetaType::UnknownType);
    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), metaType);
    QCOMPARE(result.value<QList<QString>>(), QList<QString>() << "foo" << "bar" << "baz");
}

void TestDocumentStreamReader::testReadListOfIntValue()
{

}

void TestDocumentStreamReader::testReadListOfDoubleValue()
{

}

void TestDocumentStreamReader::testReadListOfBoolValue()
{

}

void TestDocumentStreamReader::testReadListOfEnumValue()
{

}

void TestDocumentStreamReader::testReadListOfFlagValue()
{

}

void TestDocumentStreamReader::testReadListOfPointValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<item><x>1.1</x><y>2.2</y></item>"
                    "<item><x>3.3</x><y>4.4</y></item>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readListOfPointValue();

    int metaType = qRegisterMetaType<QList<QPointF>>();
    QVERIFY(metaType != QMetaType::UnknownType);
    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), metaType);
    QCOMPARE(result.value<QList<QPointF>>(), QList<QPointF>() << QPointF(1.1, 2.2) << QPointF(3.3, 4.4));
}

void TestDocumentStreamReader::testReadListOfSizeValue()
{

}

void TestDocumentStreamReader::testReadListOfStringListValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<item><string>foo</string><string>bar</string></item>"
                    "<item><string>baz</string></item>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readListOfStringListValue();

    int metaType = qRegisterMetaType<QList<QStringList>>();
    QVERIFY(metaType != QMetaType::UnknownType);
    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), metaType);
    QCOMPARE(result.value<QList<QStringList>>(),
             QList<QStringList>()
             << (QStringList() << "foo" << "bar")
             << (QStringList() << "baz"));
}

void TestDocumentStreamReader::testReadStringPropertyValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "foo bar baz"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readPropertyValue(DocumentObjectMock1::staticMetaObject.property(3));

    QCOMPARE(result.type(), QVariant::String);
    QCOMPARE(result.value<QString>(), QString("foo bar baz"));
}

void TestDocumentStreamReader::testReadDoublePropertyValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "1.234"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readPropertyValue(DocumentObjectMock1::staticMetaObject.property(4));

    QCOMPARE(result.type(), QVariant::Double);
    QCOMPARE(result.value<double>(), 1.234);
}

void TestDocumentStreamReader::testReadBoolPropertyValue()
{

}

void TestDocumentStreamReader::testReadIntPropertyValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "-42"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readPropertyValue(DocumentObjectMock1::staticMetaObject.property(5));

    QCOMPARE(result.type(), QVariant::Int);
    QCOMPARE(result.value<int>(), -42);
}

void TestDocumentStreamReader::testReadEnumPropertyValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "MockEnum4"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readPropertyValue(DocumentObjectMock1::staticMetaObject.property(6));

    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), QMetaType::type("DocumentObjectMock1::MockEnum"));
    QCOMPARE(result.value<DocumentObjectMock1::MockEnum>(), DocumentObjectMock1::MockEnum4);
}

void TestDocumentStreamReader::testReadFlagPropertyValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "MockFlag1|MockFlag3"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readPropertyValue(DocumentObjectMock1::staticMetaObject.property(7));

    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), QMetaType::type("DocumentObjectMock1::MockFlags"));
    QCOMPARE(result.value<DocumentObjectMock1::MockFlags>(), DocumentObjectMock1::MockFlag1|DocumentObjectMock1::MockFlag3);
}

void TestDocumentStreamReader::testReadPointPropertyValue()
{

}

void TestDocumentStreamReader::testReadSizePropertyValue()
{

}

void TestDocumentStreamReader::testReadStringListPropertyValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<string>foo</string><string>bar</string><string>baz</string>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readPropertyValue(DocumentObjectMock1::staticMetaObject.property(8));

    QCOMPARE(result.type(), QVariant::StringList);
    QCOMPARE(result.value<QStringList>(), QStringList() << "foo" << "bar" << "baz");
}

void TestDocumentStreamReader::testReadListOfStringPropertyValue()
{

}

void TestDocumentStreamReader::testReadListOfIntPropertyValue()
{

}

void TestDocumentStreamReader::testReadListOfDoublePropertyValue()
{

}

void TestDocumentStreamReader::testReadListOfBoolPropertyValue()
{

}

void TestDocumentStreamReader::testReadListOfEnumPropertyValue()
{

}

void TestDocumentStreamReader::testReadListOfFlagPropertyValue()
{

}

void TestDocumentStreamReader::testReadListOfPointPropertyValue()
{

}

void TestDocumentStreamReader::testReadListOfSizePropertyValue()
{

}

void TestDocumentStreamReader::testReadListOfStringListPropertyValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<item><string>foo</string><string>bar</string></item>"
                    "<item><string>baz</string></item>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.readNextStartElement();
    QVariant result = reader.readPropertyValue(DocumentObjectMock4::staticMetaObject.property(6));

    int metaType = qRegisterMetaType<QList<QStringList>>();
    QVERIFY(metaType != QMetaType::UnknownType);
    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), metaType);
    QCOMPARE(result.value<QList<QStringList>>(),
             QList<QStringList>()
             << (QStringList() << "foo" << "bar")
             << (QStringList() << "baz"));
}

void TestDocumentStreamReader::testReadMockObject0()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.m_document = new LDO::Document();
    reader.m_document->registerFactory(new DocumentObjectFactoryMock());
    reader.readNextStartElement();
    LDO::IDocumentObject *result = reader.readObject(DocumentObjectMock0::staticMetaObject);
    QVERIFY(result != nullptr);
    QVERIFY(qobject_cast<DocumentObjectMock0*>(result) != nullptr);
}

void TestDocumentStreamReader::testReadMockObject1()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<property name=\"string\" type=\"QString\">foo</property>"
                    "<property name=\"real\" type=\"double\">1.2</property>"
                    "<property name=\"integer\" type=\"int\">42</property>"
                    "<property name=\"enumeration\" type=\"DocumentObjectMock1::MockEnum\">MockEnum8</property>"
                    "<property name=\"flags\" type=\"DocumentObjectMock1::MockFlags\">MockFlag1|MockFlag3</property>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.m_document = new LDO::Document();
    reader.m_document->registerFactory(new DocumentObjectFactoryMock());
    reader.readNextStartElement();
    LDO::IDocumentObject *result = reader.readObject(DocumentObjectMock1::staticMetaObject);

    QVERIFY(result != nullptr);
    QVERIFY(qobject_cast<DocumentObjectMock1*>(result) != nullptr);
    auto mock = qobject_cast<DocumentObjectMock1*>(result);
    QCOMPARE(mock->string(), QString("foo"));
    QCOMPARE(mock->real(), 1.2);
    QCOMPARE(mock->integer(), 42);
    QCOMPARE(mock->enumeration(), DocumentObjectMock1::MockEnum8);
    QCOMPARE(mock->flags(), DocumentObjectMock1::MockFlag1|DocumentObjectMock1::MockFlag3);
}

void TestDocumentStreamReader::testReadObjectStarValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<property name=\"string\" type=\"QString\">foo</property>"
                    "<property name=\"real\" type=\"double\">1.2</property>"
                    "<property name=\"integer\" type=\"int\">42</property>"
                    "<property name=\"enumeration\" type=\"DocumentObjectMock1::MockEnum\">MockEnum8</property>"
                    "<property name=\"flags\" type=\"DocumentObjectMock1::MockFlags\">MockFlag1|MockFlag3</property>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    int metaTypeId = qRegisterMetaType<DocumentObjectMock1*>();

    reader.m_document = new LDO::Document();
    reader.m_document->registerFactory(new DocumentObjectFactoryMock());
    reader.readNextStartElement();
    QVariant result = reader.readObjectStarValue("DocumentObjectMock1*");

    QVERIFY(result.isValid());
    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), metaTypeId);
    QVERIFY(result.value<DocumentObjectMock1*>() != nullptr);
    DocumentObjectMock1 *mock = result.value<DocumentObjectMock1*>();
    QCOMPARE(mock->string(), QString("foo"));
    QCOMPARE(mock->real(), 1.2);
    QCOMPARE(mock->integer(), 42);
    QCOMPARE(mock->enumeration(), DocumentObjectMock1::MockEnum8);
    QCOMPARE(mock->flags(), DocumentObjectMock1::MockFlag1|DocumentObjectMock1::MockFlag3);
}

void TestDocumentStreamReader::testReadObjectStarPropertyValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<property name=\"string\" type=\"QString\">foo</property>"
                    "<property name=\"real\" type=\"double\">1.2</property>"
                    "<property name=\"integer\" type=\"int\">42</property>"
                    "<property name=\"enumeration\" type=\"DocumentObjectMock1::MockEnum\">MockEnum8</property>"
                    "<property name=\"flags\" type=\"DocumentObjectMock1::MockFlags\">MockFlag1|MockFlag3</property>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    int metaTypeId = qRegisterMetaType<DocumentObjectMock1*>();

    reader.m_document = new LDO::Document();
    reader.m_document->registerFactory(new DocumentObjectFactoryMock());
    reader.readNextStartElement();
    QVariant result = reader.readPropertyValue(DocumentObjectMock2::staticMetaObject.property(3));

    QVERIFY(result.isValid());
    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), metaTypeId);
    QVERIFY(result.value<DocumentObjectMock1*>() != nullptr);
    DocumentObjectMock1 *mock = result.value<DocumentObjectMock1*>();
    QCOMPARE(mock->string(), QString("foo"));
    QCOMPARE(mock->real(), 1.2);
    QCOMPARE(mock->integer(), 42);
    QCOMPARE(mock->enumeration(), DocumentObjectMock1::MockEnum8);
    QCOMPARE(mock->flags(), DocumentObjectMock1::MockFlag1|DocumentObjectMock1::MockFlag3);
}

void TestDocumentStreamReader::testReadListOfObjectStarValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">foo</property>"
                    "</item>"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">bar</property>"
                    "</item>"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">baz</property>"
                    "</item>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.m_document = new LDO::Document();
    reader.m_document->registerFactory(new DocumentObjectFactoryMock());
    reader.readNextStartElement();
    int metaType = qRegisterMetaType<QList<DocumentObjectMock0*>>();
    QVariant result = reader.readListOfObjectStarValue("DocumentObjectMock0*");

    QVERIFY(metaType != QMetaType::UnknownType);
    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), metaType);
    auto list = result.value<QList<DocumentObjectMock0*>>();
    QVERIFY(list.count() == 3);
    QCOMPARE(list.at(0)->objectName(), QString("foo"));
    QCOMPARE(list.at(1)->objectName(), QString("bar"));
    QCOMPARE(list.at(2)->objectName(), QString("baz"));
}

void TestDocumentStreamReader::testReadListOfObjectStarPropertyValue()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">foo</property>"
                    "</item>"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">bar</property>"
                    "</item>"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">baz</property>"
                    "</item>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.m_document = new LDO::Document();
    reader.m_document->registerFactory(new DocumentObjectFactoryMock());
    reader.readNextStartElement();
    int metaType = qRegisterMetaType<QList<DocumentObjectMock1*>>();
    QVariant result = reader.readPropertyValue(DocumentObjectMock3::staticMetaObject.property(3));

    QVERIFY(metaType != QMetaType::UnknownType);
    QCOMPARE(result.type(), QVariant::UserType);
    QCOMPARE(result.userType(), metaType);
    auto list = result.value<QList<DocumentObjectMock1*>>();
    QVERIFY(list.count() == 3);
    QCOMPARE(list.at(0)->objectName(), QString("foo"));
    QCOMPARE(list.at(1)->objectName(), QString("bar"));
    QCOMPARE(list.at(2)->objectName(), QString("baz"));
}

void TestDocumentStreamReader::testReadMockObject2()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<property name=\"object\" type=\"DocumentObjectMock1*\">"
                     "<property name=\"string\" type=\"QString\">foo</property>"
                     "<property name=\"real\" type=\"double\">1.2</property>"
                     "<property name=\"integer\" type=\"int\">42</property>"
                     "<property name=\"enumeration\" type=\"DocumentObjectMock1::MockEnum\">MockEnum8</property>"
                     "<property name=\"flags\" type=\"DocumentObjectMock1::MockFlags\">MockFlag1|MockFlag3</property>"
                    "</property>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.m_document = new LDO::Document();
    reader.m_document->registerFactory(new DocumentObjectFactoryMock());
    reader.readNextStartElement();
    LDO::IDocumentObject *result = reader.readObject(DocumentObjectMock2::staticMetaObject);

    QVERIFY(result != nullptr);
    QVERIFY(qobject_cast<DocumentObjectMock2*>(result) != nullptr);
    DocumentObjectMock2 *mock = qobject_cast<DocumentObjectMock2*>(result);
    QVERIFY(mock->object() != nullptr);
    DocumentObjectMock1 *subMock = mock->object();
    QCOMPARE(subMock->string(), QString("foo"));
    QCOMPARE(subMock->real(), 1.2);
    QCOMPARE(subMock->integer(), 42);
    QCOMPARE(subMock->enumeration(), DocumentObjectMock1::MockEnum8);
    QCOMPARE(subMock->flags(), DocumentObjectMock1::MockFlag1|DocumentObjectMock1::MockFlag3);
}

void TestDocumentStreamReader::testReadMockObject3()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "<property name=\"objects\" type=\"QList&lt;DocumentObjectMock1*&gt;\">"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">foo</property>"
                    "</item>"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">bar</property>"
                    "</item>"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">baz</property>"
                    "</item>"
                    "</property>"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.m_document = new LDO::Document();
    reader.m_document->registerFactory(new DocumentObjectFactoryMock());
    reader.readNextStartElement();
    LDO::IDocumentObject *result = reader.readObject(DocumentObjectMock3::staticMetaObject);

    QVERIFY(result != nullptr);
    QVERIFY(qobject_cast<DocumentObjectMock3*>(result) != nullptr);
    auto mock = qobject_cast<DocumentObjectMock3*>(result);
    QVERIFY(mock->objects().count() == 3);
    QCOMPARE(mock->objects().at(0)->objectName(), QString("foo"));
    QCOMPARE(mock->objects().at(1)->objectName(), QString("bar"));
    QCOMPARE(mock->objects().at(2)->objectName(), QString("baz"));
}

void TestDocumentStreamReader::testReadMockObject4()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<test xmlns=\"https://libre-eda.org/xml-document/1.0\">\n"
                    "<property name=\"strings\" type=\"QList&lt;QString&gt;\">\n"
                    "<item>foo</item><item>bar</item>\n"
                    "</property>\n"
                    "<property name=\"doubles\" type=\"QList&lt;double&gt;\">\n"
                    "<item>1.2</item>\n"
                    "<item>3.4</item>\n"
                    "</property>\n"
                    "<property name=\"points\" type=\"QList&lt;QPointF&gt;\">\n"
                    "<item><x>1.1</x><y>2.2</y></item>\n"
                    "</property>\n"
                    "<property name=\"stringLists\" type=\"QList&lt;QStringList&gt;\">\n"
                    "<item><string>foo</string><string>bar</string></item>\n"
                    "</property>\n"
                    "</test>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    reader.m_document = new LDO::Document();
    reader.m_document->registerFactory(new DocumentObjectFactoryMock());
    reader.readNextStartElement();
    LDO::IDocumentObject *result = reader.readObject(DocumentObjectMock4::staticMetaObject);

    QVERIFY(result != nullptr);
    QVERIFY(qobject_cast<DocumentObjectMock4*>(result) != nullptr);
    auto mock = qobject_cast<DocumentObjectMock4*>(result);
    QCOMPARE(mock->strings(), QList<QString>() << "foo" << "bar");
    QCOMPARE(mock->doubles(), QList<double>() << 1.2 << 3.4);
    QCOMPARE(mock->points(), QList<QPointF>() << QPointF(1.1, 2.2));
    QCOMPARE(mock->stringLists(), QList<QStringList>() << (QStringList() << "foo" << "bar"));
}

void TestDocumentStreamReader::testReadEmptyDocument()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                    "<document xmlns=\"https://libre-eda.org/xml-document/1.0\">"
                    "</document>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    auto document = new LDO::Document();
    document->registerFactory(new DocumentObjectFactoryMock());

    bool result = reader.readDocument(document);

    QCOMPARE(result, true);
    QCOMPARE(document->childObjectCount(), 0);
}

void TestDocumentStreamReader::testReadComplexDocument()
{
    LDO::DocumentStreamReader reader;
    QByteArray data("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                    "<document xmlns=\"https://libre-eda.org/xml-document/1.0\">"

                    "<object class=\"DocumentObjectMock0*\">"
                    "</object>"

                    "<object class=\"DocumentObjectMock1*\">"
                    "<property name=\"string\" type=\"QString\">foo</property>"
                    "<property name=\"real\" type=\"double\">1.2</property>"
                    "<property name=\"integer\" type=\"int\">42</property>"
                    "<property name=\"enumeration\" type=\"DocumentObjectMock1::MockEnum\">MockEnum8</property>"
                    "<property name=\"flags\" type=\"DocumentObjectMock1::MockFlags\">MockFlag1|MockFlag3</property>"
                    "</object>"

                    "<object class=\"DocumentObjectMock2*\">"
                    "<property name=\"object\" type=\"DocumentObjectMock1*\">"
                     "<property name=\"string\" type=\"QString\">foo</property>"
                     "<property name=\"real\" type=\"double\">1.2</property>"
                     "<property name=\"integer\" type=\"int\">42</property>"
                     "<property name=\"enumeration\" type=\"DocumentObjectMock1::MockEnum\">MockEnum8</property>"
                     "<property name=\"flags\" type=\"DocumentObjectMock1::MockFlags\">MockFlag1|MockFlag3</property>"
                    "</property>"
                    "</object>"

                    "<object class=\"DocumentObjectMock3*\">"
                    "<property name=\"objects\" type=\"QList&lt;DocumentObjectMock1*&gt;\">"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">foo</property>"
                    "</item>"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">bar</property>"
                    "</item>"
                    "<item>"
                     "<property name=\"objectName\" type=\"QString\">baz</property>"
                    "</item>"
                    "</property>"
                    "</object>"

                    "<object class=\"DocumentObjectMock4*\">"
                    "<property name=\"strings\" type=\"QList&lt;QString&gt;\">\n"
                    "<item>foo</item><item>bar</item>\n"
                    "</property>\n"
                    "<property name=\"doubles\" type=\"QList&lt;double&gt;\">\n"
                    "<item>1.2</item>\n"
                    "<item>3.4</item>\n"
                    "</property>\n"
                    "<property name=\"points\" type=\"QList&lt;QPointF&gt;\">\n"
                    "<item><x>1.1</x><y>2.2</y></item>\n"
                    "</property>\n"
                    "<property name=\"stringLists\" type=\"QList&lt;QStringList&gt;\">\n"
                    "<item><string>foo</string><string>bar</string></item>\n"
                    "</property>\n"
                    "</object>"

                    "</document>");

    QBuffer buffer(&data);
    buffer.open(QBuffer::ReadOnly);
    reader.setDevice(&buffer);

    reader.setDevice(&buffer);
    reader.setStrictMode(true);
    reader.setDebugEnabled(m_debug);

    auto document = new LDO::Document();
    document->registerFactory(new DocumentObjectFactoryMock());

    bool result = reader.readDocument(document);

    QCOMPARE(result, true);
    QCOMPARE(document->childObjectCount(), 5);

    auto child0 = document->childObject(0);
    QVERIFY(qobject_cast<DocumentObjectMock0*>(child0) != nullptr);

    auto child1 = document->childObject(1);
    QVERIFY(qobject_cast<DocumentObjectMock1*>(child1) != nullptr);
    auto mock1 = qobject_cast<DocumentObjectMock1*>(child1);
    QCOMPARE(mock1->string(), QString("foo"));
    QCOMPARE(mock1->real(), 1.2);
    QCOMPARE(mock1->integer(), 42);
    QCOMPARE(mock1->enumeration(), DocumentObjectMock1::MockEnum8);
    QCOMPARE(mock1->flags(), DocumentObjectMock1::MockFlag1|DocumentObjectMock1::MockFlag3);

    auto child2 = document->childObject(2);
    QVERIFY(qobject_cast<DocumentObjectMock2*>(child2) != nullptr);
    auto mock2 = qobject_cast<DocumentObjectMock2*>(child2);
    DocumentObjectMock1 *mock2SubMock = mock2->object();
    QCOMPARE(mock2SubMock->string(), QString("foo"));
    QCOMPARE(mock2SubMock->real(), 1.2);
    QCOMPARE(mock2SubMock->integer(), 42);
    QCOMPARE(mock2SubMock->enumeration(), DocumentObjectMock1::MockEnum8);
    QCOMPARE(mock2SubMock->flags(), DocumentObjectMock1::MockFlag1|DocumentObjectMock1::MockFlag3);

    auto child3 = document->childObject(3);
    QVERIFY(qobject_cast<DocumentObjectMock3*>(child3) != nullptr);
    auto mock3 = qobject_cast<DocumentObjectMock3*>(child3);
    QVERIFY(mock3->objects().count() == 3);
    QCOMPARE(mock3->objects().at(0)->objectName(), QString("foo"));
    QCOMPARE(mock3->objects().at(1)->objectName(), QString("bar"));
    QCOMPARE(mock3->objects().at(2)->objectName(), QString("baz"));

    auto child4 = document->childObject(4);
    QVERIFY(qobject_cast<DocumentObjectMock4*>(child4) != nullptr);
    auto mock4 = qobject_cast<DocumentObjectMock4*>(child4);
    QCOMPARE(mock4->strings(), QList<QString>() << "foo" << "bar");
    QCOMPARE(mock4->doubles(), QList<double>() << 1.2 << 3.4);
    QCOMPARE(mock4->points(), QList<QPointF>() << QPointF(1.1, 2.2));
    QCOMPARE(mock4->stringLists(), QList<QStringList>() << (QStringList() << "foo" << "bar"));
}
